﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OS.Instructions;

namespace OS
{
    class EntryPoint
    {
        public static ProcessControlBlock currentPCB;
        public static ProcessControlBlock inputSpoolPCB;
        public static ProcessControlBlock outputSpoolPCB = null;
        public static int cycle = 0;
        private static int timeSlice = 0;              //the time slice each program can run for continuously
        private static bool eof = false;
        private static int instrFrame = -1;
        private static String cachedCard = null;
        private static String ifbGroup = null;

        /// <summary>
        /// The entry point into the operating system.
        /// </summary>
        /// <param name="args">Program arguments</param>
        static void Main(string[] args)
        {
            Trace.WriteToTrace("Starting Operating System\n");
            CPU.SI = 3;
            QueueManager.Initialize();
            Drum.Initialize();
            ExecuteMasterMode();
        }

        static void ExecuteMasterMode()
        {
            while (true)
            {
                Trace.WriteToTrace("******************* Master Mode: Cycle " + cycle++ + " *******************");

             /* if (!QueueManager.IsReadyQueueEmpty())
                {
                    ServiceInterrupts();
                } */

                //if there is no program to execute, do not enter slave mode
                if (!QueueManager.IsReadyQueueEmpty())
                {
                    ExecuteUserProgram();
                }

                //Check interrupts
                CheckOSRegisters();

                //check the channels
                CheckHardware();

                CheckPCBQueue();

                if (QueueManager.AllQueuesEmpty() && cycle > 800 && outputSpoolPCB == null)
                {
                    while (true) ;
                }
            }

        }//end ExecuteMasterMode
        
        /// <summary>
        /// Fetch the next instruction from memory, increment the instruction register, execute the instruction
        /// </summary>
        static void ExecuteUserProgram()
        {
            Trace.WriteToTrace("Entering Slave Mode for process " + QueueManager.readyQueue.Peek().groupID);
            Trace.WriteToTrace("Process time: " + QueueManager.readyQueue.Peek().time);

            //set the registers to the PCB of the current program
            QueueManager.readyQueue.Peek().StoreRegistersToCPU();

            ///////////////
            //   FETCH   //
            ///////////////

            //Set the instruction register
            try
            {
                if (QueueManager.readyQueue.Peek().TLB[CPU.instructionCounter_IC / 10] is Track)
                {
                    throw new PageFaultException();
                }
                else if (QueueManager.readyQueue.Peek().TLB[CPU.instructionCounter_IC / 10] is Frame)
                {
                    //get the frame
                    Frame f = (Frame)QueueManager.readyQueue.Peek().TLB[CPU.instructionCounter_IC / 10];
                    //update the frames last accessed time
                    f.LastAccessed = cycle;
                    instrFrame = f.ID;
                    //store back into TLB
                    QueueManager.readyQueue.Peek().TLB[CPU.instructionCounter_IC / 10] = f;

                    CPU.instructionRegister_IR = UserStorage.ReadFromMemory(CPU.instructionCounter_IC, QueueManager.readyQueue.Peek().register_PTR);
                    Trace.WriteToTrace(CPU.instructionRegister_IR + " stored in Intsruction Register.");
                }
                else
                {
                    //shouldn't get here
                    Console.WriteLine("Value must be a Track or Frame.");
                    return;
                }
            }
            catch (KeyNotFoundException)
            {
                Trace.WriteToTrace("Page fault in fetch cycle. Setting PI to 3.");
                CPU.PI = 3;
                QueueManager.readyQueue.Peek().fetchFault = true;
                QueueManager.readyQueue.Peek().GetRegistersFromCPU();
                Trace.WriteToTrace("Exiting Slave Mode for process " + QueueManager.readyQueue.Peek().groupID);
                return;
            }
            catch (PageFaultException)
            {
                Trace.WriteToTrace("Page fault in fetch cycle. Setting PI to 3.");
                CPU.PI = 3;
                QueueManager.readyQueue.Peek().fetchFault = true;
                QueueManager.readyQueue.Peek().GetRegistersFromCPU();
                Trace.WriteToTrace("Exiting Slave Mode for process " + QueueManager.readyQueue.Peek().groupID);
                return;
            }

            Instruction instruction = new Instruction(CPU.instructionRegister_IR);
            QueueManager.readyQueue.Peek().register_IR = CPU.instructionRegister_IR;

            ///////////////////
            //   INCREMENT   //
            ///////////////////

            CPU.instructionCounter_IC++;
            Trace.WriteToTrace("Instruction Counter incremented to " + CPU.instructionCounter_IC);

            /////////////////
            //   EXECUTE   //
            /////////////////
            if (CPU.PI == 0)
            {
                if (instruction.ValidInstructions.ContainsKey(instruction.TheInstruction))
                {
                    Instruction newType = instruction.ValidInstructions[instruction.TheInstruction];
                    instruction = ChangeType(instruction, newType);
                    instruction.ExecuteInstruction();

                    if(!QueueManager.readyQueue.Peek().terminationType.Equals(""))
                    {
                        Trace.WriteToTrace("Process " + QueueManager.readyQueue.Peek().groupID + " encountered error:  " + QueueManager.readyQueue.Peek().terminationType);
                        ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                        Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminated Queue.");
                        QueueManager.terminatedQueue.Enqueue(p);
                        CPU.PI = CPU.SI = CPU.TI = 0;
                        Trace.WriteToTrace("Exiting Slave Mode for process " + p.groupID);
                        return;
                    }

                    if (CPU.PI != 3)
                    {
                        Trace.WriteToTrace("Process time incremented to " + ++QueueManager.readyQueue.Peek().time);
                    }
                }
                else
                {
                    //abort invalid operation
                    Trace.WriteToTrace(instruction.TheInstruction + " is an invalid operation. Setting PI to 1.");
                    CPU.PI = 1;
                }
            }

            //check time limit
            if (QueueManager.readyQueue.Peek().maxTime < QueueManager.readyQueue.Peek().time)
            {
                //Move the PCB from the ready queue to the terminated queue
                QueueManager.readyQueue.Peek().terminationType = Termination.TimeLimit;
                Trace.WriteToTrace("Process " + QueueManager.readyQueue.Peek().groupID + " has exceeded the time limit. Setting TI to 2.");
                CPU.TI = 2;
                timeSlice = 0;
                Trace.WriteToTrace("Exiting Slave Mode for process " + QueueManager.readyQueue.Peek().groupID);
                return;
            }
            else
            {
                //increment the time slice
                timeSlice++;
                Trace.WriteToTrace("Time slice incremented to " + timeSlice);

                //check the time slice
                if (timeSlice == 10)
                {
                    CPU.TI = 1;

                    Trace.WriteToTrace("Process " + QueueManager.readyQueue.Peek().groupID + " has used it's time sliced.");
                    Trace.WriteToTrace("TI changed from " + CPU.TI + " to 1");
                }
            }

            QueueManager.readyQueue.Peek().GetRegistersFromCPU();
            Trace.WriteToTrace("Exiting Slave Mode for process " + QueueManager.readyQueue.Peek().groupID);
        }//end ExecuteUserProgram

        /// <summary>
        /// Checks to see if the channels have completed. The OS tries to free up
        /// resources, and therefore will check Channel 2, Channel3, and Channel 1
        /// in that order.
        /// </summary>
        public static void CheckHardware()
        {
            //check to see if Channel 2 is busy
            if (Channel2.BusyFlag == false)
            {
                //if channel 2 can be assigned a task, assign it
                if (!QueueManager.IsOFBQueueEmpty())
                {
                    StartChannel2();
                    Trace.WriteToTrace("Starting Channel 2.");
                }
                //else, do nothing
                else
                {
                    Trace.WriteToTrace("Channel 2: Not busy");
                }
            }
            else
            {
                //increment the timer
                Channel2.IncrementTimer();

                //if the time to perform the task is done
                if (Channel2.Timer == Channel2.TimeNeeded)
                {
                    //set the flag to not busy
                    Channel2.BusyFlag = false;

                    //set the IOI
                    CPU.IOI += 2;

                    Trace.WriteToTrace("Channel 2: Timer = " + Channel2.Timer + ". Channel 2 finished. IOI incremented by 2.");
                    Trace.WriteToTrace("Channel 2 is no longer busy.");
                }
                else
                {
                    Trace.WriteToTrace("Channel 2: Busy. Timer = " + Channel2.Timer);
                }
            }

            //check to see if Channel 3 is busy
            if (Channel3.BusyFlag == false)
            {
                //if one of the following queues is not empty, start channel 3
                if ((!QueueManager.IsIFBQueueEmpty()) ||
                   (!QueueManager.IsTerminatedQueueEmpty() && !QueueManager.IsEBQueueEmpty()) ||
                   (outputSpoolPCB != null && !QueueManager.IsEBQueueEmpty()) ||
                   (!QueueManager.IsIOQueueEmpty()) ||
                   (!QueueManager.IsSwapQueueEmpty()))
                {
                    StartChannel3();
                    Trace.WriteToTrace("Starting Channel 3.");
                }
                //else, do nothing
                else
                {
                    Trace.WriteToTrace("Channel 3: Not busy");
                }
            }
            else
            {
                //increment the timer
                Channel3.IncrementTimer();

                //if the time to perform the task is done
                if (Channel3.Timer == Channel3.TimeNeeded)
                {
                    //set the flag to not busy
                    Channel3.BusyFlag = false;

                    //set the IOI
                    CPU.IOI += 4;

                    Trace.WriteToTrace("Channel 3: Timer = " + Channel3.Timer + ". Channel 3 finished. IOI incremented by 4.");
                    Trace.WriteToTrace("Channel 3 is no longer busy.");
                }
                else
                {
                    Trace.WriteToTrace("Channel 3: Busy. Timer = " + Channel3.Timer);
                }
            }

            //check to see if Channel 1 is busy
            if (eof)
            {
                Trace.WriteToTrace("Channel 1: Not busy");
            }
            else if (Channel1.BusyFlag == false)
            {
                //if channel 1 can be assigned a task, assign it
                if (!QueueManager.IsEBQueueEmpty())
                {
                    StartChannel1();
                    Trace.WriteToTrace("Starting Channel 1.");
                }
                //else, do nothing
                else
                {
                    Trace.WriteToTrace("Channel 1: Not busy");
                }
            }
            else
            {
                //increment the timer
                Channel1.IncrementTimer();

                //if the time to perform the task is done
                if (Channel1.Timer == Channel1.TimeNeeded)
                {
                    //set the flag to not busy
                    Channel1.BusyFlag = false;

                    //set the IOI
                    CPU.IOI += 1;

                    Trace.WriteToTrace("Channel 1: Timer = " + Channel1.Timer + ". Channel 1 finished. IOI incremented by 1.");
                    Trace.WriteToTrace("Channel 1 is no longer busy.");
                }
                else
                {
                    Trace.WriteToTrace("Channel 1: Busy. Timer = " + Channel1.Timer);
                }
            }

            Trace.WriteToTrace("IOI = " + CPU.IOI);
        }

        public static void StartChannel1()
        {
            //set the timer to 0
            Channel1.ResetTimer();
            Trace.WriteToTrace("Channel 1 timer set to 0.");

            //set the flag to busy
            Channel1.BusyFlag = true;
            Trace.WriteToTrace("Channel 1 flag set to busy.");

            //pop the buffer from the queue
           //Buffer buffer = QueueManager.ebQueue.Dequeue();

            //store the buffer
            //Channel1.CacheBuffer(buffer);
        }

        public static void StartChannel2()
        {
            //set the timer to 0
            Channel2.ResetTimer();
            Trace.WriteToTrace("Channel 2 timer set to 0.");

            //set the flag to busy
            Channel2.BusyFlag = true;
            Trace.WriteToTrace("Channel 2 flag set to busy.");

            //pop the buffer from the queue
            //Buffer buffer = QueueManager.ofbQueue.Dequeue();

            //store the buffer
            //Channel2.CacheBuffer(buffer);
        }

        public static void StartChannel3()
        {
            //set the timer to 0
            Channel3.ResetTimer();
            Trace.WriteToTrace("Channel 3 timer set to 0.");

            //set the flag to busy
            Channel3.BusyFlag = true;
            Trace.WriteToTrace("Channel 3 flag set to busy.");

            if ((outputSpoolPCB != null && !QueueManager.IsEBQueueEmpty()) || (!QueueManager.IsTerminatedQueueEmpty() && !QueueManager.IsEBQueueEmpty()))
            {
                //buffer = QueueManager.ebQueue.Dequeue();

                //store the buffer
                //Channel3.CacheBuffer(buffer);
                Channel3.CacheType = "terminated";
            }
            else if (!QueueManager.IsIFBQueueEmpty())
            {
                Channel3.CacheType = "inputful";
            }
            else if (!QueueManager.IsIOQueueEmpty())
            {
                Channel3.CacheType = "io";
            }
            else if (!QueueManager.IsSwapQueueEmpty())
            {
                Channel3.CacheType = "swap";
            }
        }

        /// <summary>
        /// The subroutine for IR1 which is called when Channel 1 sets the IOI
        /// </summary>
        public static void SubroutineIR1()
        {
            String card;

            if (cachedCard == null)
            {
                //Read the next card from the file
                card = CardReader.ReadNextCard();
            }
            else
            {
                card = cachedCard;
                cachedCard = null;
            }

            if (card == null && !eof)
            {
                eof = true;
                Buffer b = new Buffer();
                b.Type = Buffer.TYPE.EMPTY;
                QueueManager.ebQueue.Enqueue(b);
                Trace.WriteToTrace("End of file reached. The empty buffer that being attempted to write into is returned to the Empty Buffer Queue.");
                CPU.IOI -= 1;
                Trace.WriteToTrace("Decrementing IOI by 1.");
                return;
            }

            Trace.WriteToTrace("Card Reader read in card: " + card);

            //if not end-of-file and the empty buffer queue is not empty
            if (card != null && !QueueManager.IsEBQueueEmpty())
            {
                //give line and eb to ch1
                Buffer buffer = Channel1.StoreCardInBuffer(card);

                String line = buffer.Data;

                //if the ifb is $AMJ
                if (line.StartsWith("$AMJ"))
                {
                    Trace.WriteToTrace("Initializing Process Control Block");

                    //create and init pcb
                    inputSpoolPCB = new ProcessControlBlock();
                    inputSpoolPCB.groupID = line.Substring(4, 4);
                    ifbGroup = line.Substring(4, 4);
                    inputSpoolPCB.maxTime = Convert.ToInt16(line.Substring(8, 4));
                    inputSpoolPCB.maxLines = Convert.ToInt16(line.Substring(12, 4));
                    inputSpoolPCB.frames++;
                    Trace.WriteToTrace("Process " + inputSpoolPCB.groupID + " given a frame for the Page Table. Process now has "
                        + inputSpoolPCB.frames + " frames.");
                    Random random = new Random();
                    inputSpoolPCB.register_PTR = random.Next(0, 29) * 10;

                    //allocate frame for page table
                    StorePageTable(inputSpoolPCB.register_PTR);

                    Trace.WriteToTrace("Initializing Page Table Register.");

                    //set f <- P (program card to follow)
                    Trace.WriteToTrace("F flag set to 'P' to indicate program cards to follow.");
                    inputSpoolPCB.f = 'p';

                    //change status from ifb to eb
                    buffer.ClearBuffer();
                    Trace.WriteToTrace("Inputful buffer changed to empty buffer.");

                    //return eb to ebq
                    QueueManager.ebQueue.Enqueue(buffer);
                    Trace.WriteToTrace("Empty buffer placed on Empty Buffer Queue.");
                }
                //if the ifb is $DTA
                else if (line.StartsWith("$DTA"))
                {
                    //set f <- D (data card to follow)
                    inputSpoolPCB.f = 'd';
                    Trace.WriteToTrace("F flag set to 'D' to indicate data cards to follow.");

                    //change status from ifb to eb
                    buffer.ClearBuffer();
                    Trace.WriteToTrace("Inputful buffer changed to empty buffer.");

                    //return buffer to ebq
                    QueueManager.ebQueue.Enqueue(buffer);
                    Trace.WriteToTrace("Empty buffer placed on Empty Buffer Queue.");
                }
                //if the ifb is $EOJ
                else if (line.StartsWith("$EOJ"))
                {
                    //place pcb on PCB queue
                    QueueManager.pcbQueue.Enqueue(new ProcessControlBlock(inputSpoolPCB));
                    
                    Trace.WriteToTrace("Process " + inputSpoolPCB.groupID + " placed on PCBQueue.");
                    inputSpoolPCB = null;
                    ifbGroup = null;

                    //change status from ifb to eb
                    buffer.ClearBuffer();
                    Trace.WriteToTrace("Inputful buffer changed to empty buffer.");

                    //return buffer to ebq
                    QueueManager.ebQueue.Enqueue(buffer);
                    Trace.WriteToTrace("Empty buffer placed on Empty Buffer Queue.");
                }
                else
                {
                    //place ifb on ifbq, save f information (program or data card) for channel 3

                    while (buffer.Data.Length < 40)
                    {
                        buffer.Data += " ";
                    }

                    IFBBuffer ifbb = Buffer.ToIFBBuffer(buffer);
                    ifbb.F = inputSpoolPCB.f;

                    if (inputSpoolPCB.f.Equals('d'))
                    {
                        inputSpoolPCB.dataLines++;
                    }

                    QueueManager.ifbQueue.Enqueue(ifbb);
                    Trace.WriteToTrace("Inputful buffer placed on Inputful Buffer Queue");
                }
            }
            else if (QueueManager.IsEBQueueEmpty())
            {
                cachedCard = card;
            }

            CPU.IOI -= 1;
            Trace.WriteToTrace("Decrementing IOI by 1.");
        }

        /// <summary>
        /// Stores the page table in a given location
        /// </summary>
        /// <param name="location">The location to store the page table</param>
        private static void StorePageTable(int location)
        {
            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    UserStorage.PhysicalMemory[location, j] = "&";
                }

                location++;
            }

            Trace.WriteToTrace("Page table initialized and stored in frame " + location / 10);
        }

        /// <summary>
        /// The subroutine for IR2 which is called when Channel 2 sets the IOI
        /// </summary>
        public static void SubroutineIR2()
        {
            Buffer buffer = Channel2.PrintBuffer();
            QueueManager.ebQueue.Enqueue(buffer);
            Trace.WriteToTrace("Empty buffer placed on empty buffer queue.");

            //check to see if Channel 2 can start again
            if (!QueueManager.IsOFBQueueEmpty())
            {
                StartChannel2();
            }

            CPU.IOI -= 2;
            Trace.WriteToTrace("Decrementing IOI by 2.");
        }

        /// <summary>
        /// The subroutine for IR3 which is called when Channel 3 sets the IOI
        /// </summary>
        public static void SubroutineIR3()
        {
            if (Channel3.CacheType.Equals("terminated"))
            {
                if (QueueManager.terminatedQueue.Count < 1)
                {
                    Trace.WriteToTrace("Decrementing IOI by 4.");
                    CPU.IOI -= 4;
                    return;
                }
                
                if (!QueueManager.terminatedQueue.Peek().prepForPrint)
                {
                    //release all resources except lines to print
                    Trace.WriteToTrace("Process " + QueueManager.terminatedQueue.Peek().groupID + " releasing all resources besides output lines.");
                    Trace.WriteToSameLine("Frames released:");

                    for (int i = 0; i < 10; i++)
                    {
                        try
                        {
                            Frame f = new Frame(int.Parse(UserStorage.PhysicalMemory[QueueManager.terminatedQueue.Peek().register_PTR + i, 0]));
                            UserStorage.ReleaseFrame(f);
                            Trace.WriteToSameLine(" " + f.ID);
                        }
                        catch (Exception)
                        { continue; }
                    }

                    Trace.WriteToSameLine("\r\n");

                    Trace.WriteToSameLine("Tracks released:");

                    foreach (int key in QueueManager.terminatedQueue.Peek().TLB.Keys)
                    {
                        if (QueueManager.terminatedQueue.Peek().TLB[key] is Track)
                        {
                            Track trk = (Track) QueueManager.terminatedQueue.Peek().TLB[key];
                            Trace.WriteToSameLine(" " + trk.TrackID);
                            Drum.ClearTrack(trk.TrackID);
                        }
                    }

                    Trace.WriteToSameLine("\r\n");

                    Frame frm = new Frame(QueueManager.terminatedQueue.Peek().register_PTR);

                    UserStorage.ReleaseFrame(frm);
                    Trace.WriteToTrace("Page Table in frame " + frm.ID + " released.");

                    String str = LinePrinter.GenerateHeader1(QueueManager.terminatedQueue.Peek());
                    //write header lines to Track
                    Track header1 = Drum.GetFreeTrack();
                    Drum.WriteToTrack(header1, str);

                    Track header2 = Drum.GetFreeTrack();
                    Drum.WriteToTrack(header2, LinePrinter.GenerateHeader2(QueueManager.terminatedQueue.Peek()));

                    Track header3 = Drum.GetFreeTrack();
                    Drum.WriteToTrack(header3, "                                       ");

                    Track footer1 = Drum.GetFreeTrack();
                    Drum.WriteToTrack(footer1, "                                        ");

                    Track footer2 = Drum.GetFreeTrack();
                    Drum.WriteToTrack(footer2, "                                        ");

                    //update pcb
                    QueueManager.terminatedQueue.Peek().tracksOfOutput.Insert(0, header3.TrackID);
                    QueueManager.terminatedQueue.Peek().tracksOfOutput.Insert(0, header2.TrackID);
                    QueueManager.terminatedQueue.Peek().tracksOfOutput.Insert(0, header1.TrackID);
                    QueueManager.terminatedQueue.Peek().tracksOfOutput.Add(footer2.TrackID);
                    QueueManager.terminatedQueue.Peek().tracksOfOutput.Add(footer1.TrackID);

                    QueueManager.terminatedQueue.Peek().printLines = QueueManager.terminatedQueue.Peek().tracksOfOutput.Count;

                    QueueManager.terminatedQueue.Peek().prepForPrint = true;
                }

                //get the track where the data is
                if (QueueManager.terminatedQueue.Peek().prepForPrint && QueueManager.terminatedQueue.Peek().tracksOfOutput.Count > 0)
                {
                    if (!QueueManager.IsEBQueueEmpty())
                    {
                        Track track = Drum.GetTrack(QueueManager.terminatedQueue.Peek().tracksOfOutput[0]);

                        //remove the pointer to this track from the PCB
                        QueueManager.terminatedQueue.Peek().tracksToRelease.Add(QueueManager.terminatedQueue.Peek().tracksOfOutput[0]);
                        QueueManager.terminatedQueue.Peek().tracksOfOutput.RemoveAt(0);

                        QueueManager.ofbQueue.Enqueue(Channel3.Channel3OutputSpooling(track));

                        Drum.ClearTrack(track.TrackID);
                        Trace.WriteToTrace("Output line stored in track " + track.TrackID + " freed.");
                    }
                }
                else if (QueueManager.terminatedQueue.Peek().prepForPrint && QueueManager.terminatedQueue.Peek().printLines <= 0)
                {
                    Trace.WriteToSameLine("Process " + QueueManager.terminatedQueue.Peek().groupID + " releasing tracks:");
                    //release tracks of output
                    foreach (int k in QueueManager.terminatedQueue.Peek().tracksToRelease)
                    {
                        Drum.ClearTrack(k);
                        Trace.WriteToSameLine(" " + k);
                    }

                    Trace.WriteToSameLine("\r\n");

                    Trace.WriteToTrace("Process " + QueueManager.terminatedQueue.Peek().groupID + " moved off of Terminate Queue and released.");
                    //release pcb
                    QueueManager.terminatedQueue.Dequeue();
                }   
            }
            else if (Channel3.CacheType.Equals("io"))
            {
                Instruction instr = new Instruction(QueueManager.ioQueue.Peek().register_IR);

                //if the instruction is GD
                if (instr.TheInstruction.Equals("GD"))
                {
                    Trace.WriteToTrace("Executing GD instruction.");
                    int virtualAddress = int.Parse(QueueManager.ioQueue.Peek().register_IR[2].ToString() + QueueManager.ioQueue.Peek().register_IR[3].ToString());
                    int virtualPage = virtualAddress / 10;
                    int realAddress = UserStorage.VirtualToPhysical(virtualAddress, QueueManager.ioQueue.Peek().register_PTR);

                    Track track = Drum.GetTrack(QueueManager.ioQueue.Peek().d[0]);
                    Frame frame = (Frame) QueueManager.ioQueue.Peek().TLB[virtualPage];

                    Channel3.Channel3GD(track, frame);

                    UserStorage.WriteFrameToMemory(frame, realAddress);

                    Trace.WriteToTrace("Data card from track " + track.TrackID + " written to frame " + frame.ID);
                }
                //else if the instruction is PD
                else if (instr.TheInstruction.Equals("PD"))
                {
                    Trace.WriteToTrace("Executing PD instruction.");
                    int virtualAddress = int.Parse(QueueManager.ioQueue.Peek().register_IR[2].ToString() + QueueManager.ioQueue.Peek().register_IR[3].ToString());
                    int virtualPage = virtualAddress / 10;
                    int realAddress = UserStorage.VirtualToPhysical(virtualAddress, QueueManager.ioQueue.Peek().register_PTR);

                    Track track = Drum.GetFreeTrack();
                    Frame frame = UserStorage.GetFrameForRealAddress(realAddress);

                    QueueManager.ioQueue.Peek().tracksOfOutput.Add(track.TrackID);

                    Channel3.Channel3PD(track, frame);

                    Trace.WriteToTrace("Output line from frame " + frame.ID + " written to track " + track.TrackID);
                }

                Channel3.CacheType = "";
            }
            else if (Channel3.CacheType.Equals("swap"))
            {
                int page;

                if (QueueManager.swapQueue.Peek().fetchFault)
                {
                    QueueManager.swapQueue.Peek().fetchFault = false;
                    page = (QueueManager.swapQueue.Peek().register_IC) / 10;
                }
                else
                {
                    Instruction instr = new Instruction(QueueManager.swapQueue.Peek().register_IR);
                    page = int.Parse(instr.MemoryLocation) / 10;

                    //adjust IC
                    QueueManager.swapQueue.Peek().register_IC--;
                    Trace.WriteToTrace("IC decremented to " + QueueManager.swapQueue.Peek().register_IC + " from " + (QueueManager.swapQueue.Peek().register_IC + 1));
                }

                if(QueueManager.swapQueue.Peek().frames < QueueManager.swapQueue.Peek().maxFrames)
                {
                    //allocate a new frame
                    int frameNumber = AllocateNewFrame(QueueManager.swapQueue.Peek());

                    if (frameNumber != -1)
                    {
                        Trace.WriteToTrace("Process has not used all of its frames. Allocating frame " + frameNumber + ".");

                        //frame is available
                        Frame newlyAllocatedFrame = new Frame(frameNumber);

                        //swap track to frame and update TLB
                        if (QueueManager.swapQueue.Peek().TLB.ContainsKey(page))
                        {
                            if (QueueManager.swapQueue.Peek().TLB[page] is Track)
                            {
                                Track trk = (Track)QueueManager.swapQueue.Peek().TLB[page];
                                Channel3.Channel3SwapIn(trk, newlyAllocatedFrame, page);
                            }
                            else
                            {
                                while (true) ;
                            }
                        }
                        else
                        {
                            Trace.WriteToTrace("Invalid page fault.");
                            ProcessControlBlock p = QueueManager.swapQueue.Dequeue();
                            p.terminationType = Termination.InvalidPageFault;
                            Trace.WriteToTrace("Process " + p.groupID + " moved from Swap Queue to Terminate Queue.");
                            QueueManager.terminatedQueue.Enqueue(p);
                            return;
                        }
                    }
                    else
                    {
                        Trace.WriteToTrace("No frame available. Must find victim page.");

                        //find a victim page
                        Frame frame = FindVictimPage(QueueManager.swapQueue.Peek());

                        //get a free drum track
                        Track track = Drum.GetFreeTrack();
                        Trace.WriteToTrace("Free drum track found at track " + track.TrackID);

                        Channel3.Channel3SwapOut(frame, track, page);
                    }
                }
                else
                {
                    Trace.WriteToTrace("Process already has 4 frames. Must find victim page.");

                    //find a victim page
                    Frame frame = FindVictimPage(QueueManager.swapQueue.Peek());

                    //get a free drum track
                    Track track = Drum.GetFreeTrack();

                    Channel3.Channel3SwapOut(frame, track, page);
                }
            }
            else if (Channel3.CacheType.Equals("inputful"))
            {
                //get a free track on the drum
                Track track = Drum.GetFreeTrack();
                Trace.WriteToTrace("Free track on drum found: Track " + track.TrackID);

                //run Channel 3's part of the IS subroutine
                IFBBuffer buffer = Channel3.Channel3InputSpooling(track);

                //change status from ifb to eb
                Trace.WriteToTrace("Inputful buffer changed to empty buffer");

                if(QueueManager.IsPCBQueueEmpty())
                {
                    //place track number in P or D part of PCB
                    if (buffer.F == 'p')
                    {
                        inputSpoolPCB.p.Add(track.TrackID);
                        Trace.WriteToTrace(track.TrackID + " placed in the p section of process " + inputSpoolPCB.groupID);
                        inputSpoolPCB.TLB.Add(inputSpoolPCB.TLB.Count, track);
                    }
                    else if (buffer.F == 'd')
                    {
                        inputSpoolPCB.d.Add(track.TrackID);
                        Trace.WriteToTrace(track.TrackID + " placed in the d section of process " + inputSpoolPCB.groupID);
                    }

                    //put emptry buffer on empty buffer queue
                    Buffer b = new Buffer();
                    b.ClearBuffer();
                    b.Type = Buffer.TYPE.EMPTY;
                    QueueManager.ebQueue.Enqueue(b);
                    Trace.WriteToTrace("Empty buffer placed on Empty Buffer Queue");
                }
                else
                {
                    //place track number in P or D part of PCB
                    if (buffer.F == 'p')
                    {
                        QueueManager.pcbQueue.Peek().p.Add(track.TrackID);
                        Trace.WriteToTrace(track.TrackID + " placed in the p section of process " + QueueManager.pcbQueue.Peek().groupID);
                        QueueManager.pcbQueue.Peek().TLB.Add(QueueManager.pcbQueue.Peek().TLB.Count, track);
                    }
                    else if (buffer.F == 'd')
                    {
                        QueueManager.pcbQueue.Peek().d.Add(track.TrackID);
                        Trace.WriteToTrace(track.TrackID + " placed in the d section of process " + QueueManager.pcbQueue.Peek().groupID);
                    }

                    //put emptry buffer on empty buffer queue
                    Buffer b = new Buffer();
                    b.ClearBuffer();
                    b.Type = Buffer.TYPE.EMPTY;
                    QueueManager.ebQueue.Enqueue(b);
                    Trace.WriteToTrace("Empty buffer placed on Empty Buffer Queue");
                }
            }
            else
            {
                //Console.WriteLine("EntryPoint: SubroutineIR3: Should not have reached here.");
            }

            CPU.IOI -= 4;
            Trace.WriteToTrace("Decrementing IOI by 4.");

            //see if a new task can be assigned
            if (!QueueManager.IsIFBQueueEmpty() || !QueueManager.IsOFBQueueEmpty()
                || !QueueManager.IsIOQueueEmpty() || !QueueManager.IsSwapQueueEmpty())
            {
                StartChannel3();
                Trace.WriteToTrace("Starting Channel 3.");
            }
        }

        //page table and the one 
        public static Frame FindVictimPage(ProcessControlBlock p)
        {
            if (p.frames <= 2)
            {
                //no page to swap out
                Console.Write("EntryPoint: FindVictimPage: Error");
                return null;
            }
            else
            {
                List<Frame> frmList = new List<Frame>();

                //loop over the TLB and pull out the frames

                //NEED TO CHANGE THIS. INSTEAD OF CHECKING IF TLB HAS I, CHECK TO SEE WHAT I GOES TO

                foreach(int key in p.TLB.Keys)
                {
                    if (p.TLB[key] is Frame)
                    {
                        frmList.Add((Frame)p.TLB[key]);
                    }
                }

                //remove the instruction frame from that list
                for (int i = 0; i < frmList.Count; i++)
                {
                    if (frmList[0].ID == instrFrame)
                    {
                        frmList.Remove(frmList[0]);
                    }
                }

                //temporarily assume the first frame is the victim
                Frame victim = frmList[0];

                //loop over list of frames and see which was accessed longest ago
                for(int i = 1; i < frmList.Count; i++)
                {
                    if (frmList[i].LastAccessed < victim.LastAccessed)
                    {
                        victim = frmList[i];
                    }
                }

                Trace.WriteToTrace("Victim page: " + victim.ID);
                return victim;
            }
        }

        /// <summary>
        /// Changes the type of the instruction
        /// </summary>
        /// <param name="instr">The instruction whose type to change</param>
        /// <param name="newType">The instruction of the type to change to</param>
        /// <returns>The instruction with its type changed</returns>
        public static Instruction ChangeType(Instruction instr, Instruction newType)
        {
            Instruction temp = instr;
            instr = newType;

            instr.TheInstruction = temp.TheInstruction;
            instr.MemoryLocation = temp.MemoryLocation;

            return instr;
        }//end ChangeType

        public static void CheckOSRegisters()
        {
            //kernel booting
            if (CPU.SI == 3 && currentPCB == null)
            {
                LoadKernel();
            }

            if ((CPU.TI == 0 || CPU.TI == 1) && CPU.SI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                QueueManager.ioQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the IO Queue");
                timeSlice = 0;
                Channel3.CacheType = "io";
                CPU.ClearInterruptRegisters();
            }
            else if ((CPU.TI == 0 || CPU.TI == 1) && CPU.SI == 2)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                QueueManager.ioQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the IO Queue");
                timeSlice = 0;
                Channel3.CacheType = "io";
                CPU.ClearInterruptRegisters();
            }
            else if ((CPU.TI == 0 || CPU.TI == 1) && CPU.SI == 3)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.Normal;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if (CPU.TI == 2 && CPU.SI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeLimit;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                Channel3.CacheType = "terminated";
                CPU.ClearInterruptRegisters();
            }
            else if (CPU.TI == 2 && CPU.SI == 2)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeLimit;
                QueueManager.ioQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the IO Queue");
                timeSlice = 0;
                Channel3.CacheType = "io";
                CPU.ClearInterruptRegisters();
            }
            else if (CPU.TI == 2 && CPU.SI == 3)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.Normal;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                Channel3.CacheType = "terminated";
                CPU.ClearInterruptRegisters();
            }
            else if ((CPU.TI == 0 || CPU.TI == 1) && CPU.PI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.InvalidOperation;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                Channel3.CacheType = "terminated";
                CPU.ClearInterruptRegisters();
            }
            else if ((CPU.TI == 0 || CPU.TI == 1) && CPU.PI == 2)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.InvalidOperand;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                Channel3.CacheType = "terminated";
                CPU.ClearInterruptRegisters();
            }
            else if ((CPU.TI == 0 || CPU.TI == 1) && CPU.PI == 3)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                QueueManager.swapQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Swap Queue");
                Channel3.CacheType = "swap";
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
                /*if (!QueueManager.readyQueue.Peek().IsPEmpty())
                {
                    //if the track is free, invalid page fault?
                    if (!Drum.IsTrackFree(QueueManager.readyQueue.Peek().p[0]))
                    {
                        QueueManager.readyQueue.Peek().ClearInterrupts();
                        ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                        QueueManager.swapQueue.Enqueue(p);
                        Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Swap Queue");
                        Channel3.CacheType = "swap";
                        timeSlice = 0;
                        CPU.ClearInterruptRegisters();
                    }
                    else
                    {
                        //temporarily like this
                        QueueManager.readyQueue.Peek().ClearInterrupts();
                        ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                        p.terminationType = Termination.InvalidPageFault;
                        QueueManager.terminatedQueue.Enqueue(p);
                        Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                        timeSlice = 0;
                        Channel3.CacheType = "terminated";
                        CPU.ClearInterruptRegisters();
                    }
                }*/
            }
            else if (CPU.TI == 2 && CPU.PI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeAndOperation;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                Channel3.CacheType = "terminated";
                CPU.ClearInterruptRegisters();
            }
            else if (CPU.TI == 2 && CPU.PI == 2)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeAndOperand;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                Channel3.CacheType = "terminated";
                CPU.ClearInterruptRegisters();
            }
            else if (CPU.TI == 2 && CPU.PI == 3)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeLimit;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                Channel3.CacheType = "terminated";
                CPU.ClearInterruptRegisters();
            }
            else if (CPU.TI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                Trace.WriteToTrace("Time Slice Counter reset to 0.");
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                QueueManager.readyQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the beginning of Ready Queue to the end of the Ready Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if (CPU.TI == 2)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                Channel3.CacheType = "terminated";
                CPU.ClearInterruptRegisters();
            }

            if (CPU.IOI == 1)
            {
                SubroutineIR1();
            }
            else if (CPU.IOI == 2)
            {
                SubroutineIR2();
            }
            else if (CPU.IOI == 3)
            {
                SubroutineIR2();
                SubroutineIR1();
            }
            else if (CPU.IOI == 4)
            {
                SubroutineIR3();
            }
            else if (CPU.IOI == 5)
            {
                SubroutineIR3();
                SubroutineIR1();
            }
            else if (CPU.IOI == 6)
            {
                SubroutineIR2();
                SubroutineIR3();
            }
            else if (CPU.IOI == 7)
            {
                SubroutineIR2();
                SubroutineIR3();
                SubroutineIR1();
            }
        }

        public static int AllocateNewFrame(ProcessControlBlock p)
        {
            List<int> freeBlocks = new List<int>();

            //randomly generate a frame number
            Random random = new Random();

            //figures out which blocks in memory are free
            for (int i = 0; i < 30; i++)
            {
                if (UserStorage.PhysicalMemory[i * 10, 0] == null)
                {
                    freeBlocks.Add(i * 10);
                }
            }

            //check to see if there are any open blocks
            if (freeBlocks.Count == 0)
            {
                //no free frames to give
                return -1;
            }
            else
            {
                int temp = random.Next(0, freeBlocks.Count);
                int frame = freeBlocks[temp];

                return frame;
            }
        }

        public static void LoadKernel()
        {
            currentPCB = new ProcessControlBlock();
            currentPCB.groupID = null;

            CPU.SI = 0;
            Trace.WriteToTrace("Loading Kernel...");
            Trace.WriteToTrace("SI changed from 3 to 0.");
        }//end LoadKernel

        public static void ServiceInterrupts()
        {
            if ((QueueManager.readyQueue.Peek().register_TI == 0 || QueueManager.readyQueue.Peek().register_TI == 1) && QueueManager.readyQueue.Peek().register_SI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                QueueManager.ioQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the IO Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if ((QueueManager.readyQueue.Peek().register_TI == 0 || QueueManager.readyQueue.Peek().register_TI == 1) && QueueManager.readyQueue.Peek().register_SI == 2)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                QueueManager.ioQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the IO Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if ((QueueManager.readyQueue.Peek().register_TI == 0 || QueueManager.readyQueue.Peek().register_TI == 1) && QueueManager.readyQueue.Peek().register_SI == 3)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.Normal;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if (QueueManager.readyQueue.Peek().register_TI == 2 && QueueManager.readyQueue.Peek().register_SI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeLimit;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if (QueueManager.readyQueue.Peek().register_TI == 2 && QueueManager.readyQueue.Peek().register_SI == 2)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeLimit;
                QueueManager.ioQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the IO Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if (QueueManager.readyQueue.Peek().register_TI == 2 && QueueManager.readyQueue.Peek().register_SI == 3)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.Normal;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if ((QueueManager.readyQueue.Peek().register_TI == 0 || QueueManager.readyQueue.Peek().register_TI == 1) && QueueManager.readyQueue.Peek().register_PI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.InvalidOperation;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if ((QueueManager.readyQueue.Peek().register_TI == 0 || QueueManager.readyQueue.Peek().register_TI == 1) && QueueManager.readyQueue.Peek().register_PI == 2)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.InvalidOperand;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if ((QueueManager.readyQueue.Peek().register_TI == 0 || QueueManager.readyQueue.Peek().register_TI == 1) && QueueManager.readyQueue.Peek().register_PI == 3)
            {
                if (!QueueManager.readyQueue.Peek().IsPEmpty())
                {
                    //if the track is free, invalid page fault?
                    if (!Drum.IsTrackFree(QueueManager.readyQueue.Peek().p[0]))
                    {
                        QueueManager.readyQueue.Peek().ClearInterrupts();
                        ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                        QueueManager.swapQueue.Enqueue(p);
                        Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Swap Queue");
                        timeSlice = 0;
                        CPU.ClearInterruptRegisters();
                    }
                    else
                    {
                        //temporarily like this
                        QueueManager.readyQueue.Peek().ClearInterrupts();
                        ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                        p.terminationType = Termination.InvalidPageFault;
                        QueueManager.terminatedQueue.Enqueue(p);
                        Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                        timeSlice = 0;
                        CPU.ClearInterruptRegisters();
                    }
                }
            }
            else if (QueueManager.readyQueue.Peek().register_TI == 2 && QueueManager.readyQueue.Peek().register_PI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeAndOperation;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if (QueueManager.readyQueue.Peek().register_TI == 2 && QueueManager.readyQueue.Peek().register_PI == 2)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeAndOperand;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if (QueueManager.readyQueue.Peek().register_TI == 2 && QueueManager.readyQueue.Peek().register_PI == 3)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                p.terminationType = Termination.TimeLimit;
                QueueManager.terminatedQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the Ready Queue to the Terminate Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
            else if (QueueManager.readyQueue.Peek().register_TI == 1)
            {
                QueueManager.readyQueue.Peek().ClearInterrupts();
                ProcessControlBlock p = QueueManager.readyQueue.Dequeue();
                QueueManager.readyQueue.Enqueue(p);
                Trace.WriteToTrace("Process " + p.groupID + " moved from the beginning of Ready Queue to the end of the Ready Queue");
                timeSlice = 0;
                CPU.ClearInterruptRegisters();
            }
        }

        public static void CheckPCBQueue()
        {
            if (!QueueManager.IsPCBQueueEmpty())
            {
                if(QueueManager.pcbQueue.Peek().dataLines == QueueManager.pcbQueue.Peek().d.Count)
                {
                    //move pcb from pcbq to readyq
                    ProcessControlBlock p = QueueManager.pcbQueue.Dequeue();
                    Trace.WriteToTrace("All cards have been input spooled for process " + p.groupID);
                    Trace.WriteToTrace("Process " + p.groupID + " moved from the PCB Queue to the Ready Queue.");
                    QueueManager.readyQueue.Enqueue(p);
                }
                else if (QueueManager.IsIFBQueueEmpty())
                {
                    //move pcb from pcbq to readyq
                    ProcessControlBlock p = QueueManager.pcbQueue.Dequeue();
                    Trace.WriteToTrace("All cards have been input spooled for process " + p.groupID);
                    Trace.WriteToTrace("Process " + p.groupID + " moved from the PCB Queue to the Ready Queue.");
                    QueueManager.readyQueue.Enqueue(p);
                }
                else if (QueueManager.pcbQueue.Peek().groupID.Equals(QueueManager.ifbQueue.Peek().GroupID))
                {
                    //move pcb from ocbq to readyq
                    ProcessControlBlock p = QueueManager.pcbQueue.Dequeue();
                    Trace.WriteToTrace("All cards have been input spooled for process " + p.groupID);
                    Trace.WriteToTrace("Process " + p.groupID + " moved from the PCB Queue to the Ready Queue.");
                    QueueManager.readyQueue.Enqueue(p);
                }
            }
        }

    }//end EntryPoint
}//end OS